#ifndef  _PICTURE_H_
#define  _PICTURE_H_

#include<cstdint>
#include<string>
#include<vector>
#include<opencv2/opencv.hpp>

uint64_t CalculateHammingDistance(const unsigned char* d1,
                                  const unsigned char* d2,
                                  int length);

/*
*	PICTURE class to store key point and descriptor
*	for a picture from .key file
*
*/

// camera model define
struct Camera {
  cv::Matx33d r;
  cv::Vec3d t;
  double f, k[3];  // focal length. k1, k2, k3
  double ppx, ppy;

  Camera() {
    r = r.diag(1.0);
    t = cv::Vec3d{0.0, 0.0, 0.0};
    f = -1.0f;
    ppx = 0; ppy = 0;
    k[0] = k[1] = k[2] = 0.0f;
  }
};

struct KeyPoint
{
	//origin is the left-up corner
	//in .key file  y  x  scale  orientation
	float y;
	float x; //x is col
	float scale;
	float orientation;//(in radians from - PI to PI)
  KeyPoint() {}
  ~KeyPoint() {}
  KeyPoint(float _x, float _y, float _s, float _o) :x(_x), y(_y), scale(_s), orientation(_o) {}
};

struct Descriptor
{
	static int desc_legth;
  unsigned char* ptrDesc;

	void ClearDesc(){
		if (ptrDesc != nullptr){
			delete[] ptrDesc;
			ptrDesc = nullptr;
		}
	}

  Descriptor(){
		ptrDesc = nullptr;
	}

  ~Descriptor(){
		ClearDesc();
	}
	
	//copy constructor
  Descriptor(const Descriptor& d);

	//copy assignment
  Descriptor& operator=(const Descriptor& d);

	//move constructor
  Descriptor(Descriptor&& d):ptrDesc(d.ptrDesc){
		d.ptrDesc = nullptr;
	}

	//move assignment 
  Descriptor& operator=(Descriptor&& d);

  // copy descriptor to pointer
  unsigned char* CopyDescData(unsigned char* dst) const;
};


//store a picture's key points and descriptor
class Picture
{
private:
  int	 mImage_height;
  int  mImage_width;
  int  mIndex;

  bool mDdImageFlag;

  std::string mPicture_name;
  //origin is the left-up corner
  std::vector<KeyPoint>		mKeypoints;
  std::vector<Descriptor>	mDescriptors;

public:
  std::map<int, cv::Point3d> mPoints;

public:
	//default constructor
  Picture();

  ~Picture();

	//load descriptor from the .feat and .desc file
  bool LoadKeyPointAndDesc( const std::string& filename, bool bLoadDesc = true );
  bool LoadFeat( const std::string& key_file );
  bool LoadDesc( const std::string& desc_file );

  // copy descriptor to pointer
  unsigned char* CopyDescData(unsigned char* dst) const;

  // set index
  void SetIndex(int idx) {
    mIndex = idx;
  }
  int Index() const {
    return mIndex;
  }

	//set size
  void SetImageSize(const int width, const int height);
	//get size
  void GetImageSize(int& width, int& height) const;

  void SetFlag(const bool flag) {
    mDdImageFlag = flag;
  }

  bool Flag() const {
    return mDdImageFlag;
  }

	//the num of feature points of the picture
	size_t KeyPointsNum() const{
    return mKeypoints.size();
  }
  size_t DescNum() const {
    return mDescriptors.size();
  }

	//clear all the data
	void ClearData();

	//return the key points
  const std::vector<KeyPoint>& Keypoints() const {
    return mKeypoints;
	}

  const std::vector<Descriptor>& Descriptors() const {
    return mDescriptors;
	}

  void SetPictureName(const std::string& name) {
    mPicture_name = name;
  }

  std::string PictureName() const {
    return mPicture_name;
  }

};

//contain pictures and cameras
class AllPictures
{
private:
  std::string mKeyfile_path;  // the image key file path
  std::string	mImage_path;  // the images path
  std::string	mPicture_list_file;  // a twxt file store the picture name
  std::vector< Picture > mPictures;  // vector store all pictures database or query
  std::vector<double> mParameters;  // save parameters for each camera, ppx, ppy, f

public:
	// default constructor, set empty
  AllPictures();

	//set key file path, image file path and list file path 
  AllPictures(const std::string& key_path, const std::string& list_file, const std::string& image_path = "");

	//destructor
	~AllPictures();

  void SetKeyPath(const std::string& key_path) {
    mKeyfile_path = key_path;
  }
  void SetListFile(const std::string& list_file) {
    mPicture_list_file = list_file;
  }
  void SetImagePath(const std::string& image_path) {
    mImage_path = image_path;
  }
  void SetPath(const std::string& key_path, const std::string& list_file, const std::string& image_path = "") {
    mKeyfile_path = key_path;
    mPicture_list_file = list_file;
    mImage_path = image_path;
  }

	// load all picture keys from the list, clear if exist
  bool LoadPictures(bool bOnlyQuery = true);

  void ClearPicture() {
    mPictures.clear();
  }

	//return all pictures
	std::vector<Picture>& Pictures(){
		return mPictures;
	}
	const std::vector<Picture>& Pictures()const{
		return mPictures;
	}
  size_t PictureNum() const {
    return mPictures.size();
  }

  // return descriptor length
  size_t DescriptorLength() const {
    return Descriptor::desc_legth;
  }
  // return ppx, ppy, f parameters
  const std::vector<double>& Parameters() const {
    return mParameters;
  }
};


#endif // !_CAMERA_H_
